<?php
/**
 * Copyright (c) 2006-2007, PORTALIER Julien
 * 
 * Licensed under The LGPL License
 * Redistributions of files must retain the above copyright notice.
 * 
 * @copyright    Copyright (c) 2006-2007, Julien PORTALIER
 * @link         http://feather-cm.googlecode.com/
 * @license      http://www.opensource.org/licenses/lgpl-license.php The LGPL License
 */

class Acl extends AclComponent { }

class AclComponent /*extends BaseSingleton*/
{
	protected $id;
	protected $groups;
	
	function startup(Controller $controller)
	{
		$this->Auth = $controller->Auth;
		
		if (!empty(Load::$plugin))
			$this->Roles->Aco->prefix = Load::$plugin.'.';
		
		# has rights to access action?
		if (!empty($controller->restrictActions)
			and in_array($controller->action, $controller->restrictActions)
			and !$this->check("{$controller->viewPath}.{$controller->action}"))
		{
			$this->Auth->permissionDenied();
		}
		
		# has rights to access resource?
		if (!empty($controller->checkAccess) and array_key_exists($controller->action, $controller->checkAccess))
			$this->checkAccess("{$this->modelClass}.{$this->params['args'][0]}", $controller->checkAccess[$controller->action]);
	}
	
	/**
	 * Will prefix all ACO resources (useful for plugins).
	 */
	/*
	function setPrefix($prefix)
	{
		$this->Roles->Aco->prefix = $prefix;
	}
	*/
	
	function checkAccess($aco, $action='read')
	{
		if ($this->check($aco, null, $action) === false)
			$this->Auth->permissionDenied();
	}
	
	function check($aco=null, $aro=null, $action="read")
	{
		# privilege on X.Y?
		$right = $this->__check($aco, $aro, $action);
		if ($right)
			return true;
		
		# privilege on X.*?
		if (preg_match('/([^.]+)\..+/', $aco, $match))
		{
			$right = $this->__check("{$match[1]}.*", $aro, $action);
			if ($right)
				return true;
		}
		
		# privilege on *?
		return $this->__check('*', $aro, $action);
	}
	
	protected function __check($aco, $aro, $action)
	{
		$roles = $this->Roles->fetchRoles($aco, $aro);
		
		# restricted access? checking rules
		if (!empty($roles))
		{
			# user specific rule?
			if (isset($roles["User.{$this->Auth->id}"]))
				return $roles["User.{$this->Auth->id}"][$action] ? true : false;
			
			# groups rules
			$right = null;
			foreach ($this->Auth->groups as $grp)
			{
				if (isset($roles["Group.$grp"]))
				{
					$right = $roles["Group.$grp"][$action];
					if ($right)
						return true;
				}
			}
			if (!is_null($right))
				return $right ? true : false;
			
			# default rule?
			if (isset($roles['Group.all']))
				return $roles['Group.all'][$action] ? true : false;
			
			# access denied!
			return false;
		}
		
		// undefined state
		return null;
	}
	
	// admin
	
	function registerAro($aro)
	{
		$this->Roles->Aro->create($aro);
	}
	
	function grant($aro, $aco, $action='*', $value=true)
	{
		if ($action == '*')
		{
			$read   = $value;
			$write  = $value;
			$delete = $value;
		}
		else
		{
			foreach (explode(',', $action) as $action)
			{
				$action = trim($action);
				${$action} = $value;
			} 
		}
		return $this->Roles->create($aro, $aco,
			isset($read)   ? $read   : null,
			isset($write)  ? $write  : null,
			isset($delete) ? $delete : null
		);
	}
	
	function revoke($aro, $aco, $action='*') {
		return $this->grant($aro, $aco, $action, false);
	}
	
	function allow($aro, $aco, $action='*') {
		return $this->grant($aro, $aco, $action, true);
	}
	
	function deny($aro, $aco, $action='*') {
		return $this->grant($aro, $aco, $action, false);
	}
	
	function destroy($aco, $aro=null) {
		$this->Roles->destroy($aco, $aro);
	}
	
	// overrides
	
	function __get($name)
	{
		if ($name == 'Roles')
		{
			require_once FEATHER.'controllers'.DS.'components'.DS.'acl'.DS.'aclnode.php';
			require_once FEATHER.'controllers'.DS.'components'.DS.'acl'.DS.'role.php';
			$this->Roles = new Role();
			return $this->Roles;
		}
		return false;
	}
}
?>
